<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        // JavaScript对象及Proxy的工作原理
        let activeEffect;
        let effectStack = []
        function effect (fn, options) {
            const effectFn = () => {
                cleanup(effectFn)
                activeEffect = effectFn
                effectStack.push(effectFn)
                const res = fn()
                effectStack.pop()
                activeEffect = effectStack[effectStack.length - 1]
                return res
            }
            effectFn.options = options
            effectFn.deps = []
            if (!options || !options.lazy) {
                effectFn()
            }
            return effectFn
        } 
        function cleanup (effectFn) {
            for (let i = 0; i < effectFn.deps.length; i++) {
                const deps = effectFn.deps[i]
                deps.delete(effectFn)
            }
            effectFn.deps.length = 0
        }
        const bucket = new WeakMap()

        const jobQuene = new Set()
        const p = Promise.resolve()

        let isFlushing = false
        function flushJob () {
            if (isFlushing) return
            isFlushing = true
            p.then(() => {
                jobQuene.forEach(job => {
                    job()
                })
            }).finally(() => {
                isFlushing = false
            })
        }

        let temp1, temp2;
        function computed (getter) {
            let value;
            let dirty = true
            const effectFn = effect(getter, {
                lazy: true,
                scheduler () {
                    dirty = true
                }
            })

            const obj = {
                get value () {
                    // 只有dirty为脏时才计算值，并将得到的值缓存在value中
                    if (dirty) {
                        value = effectFn()
                        // 将dirty值设置为false，下一次访问只用缓存中的值
                        dirty = false
                    }
                    return value
                }
            }

            return obj
        }
        let obj = {
            foo: 1,
            get bar () {
                return this.foo
            }
        }
        const proxy1 = new Proxy(obj, {
            deleteProperty(target, key) {
                return Reflect.deleteProperty(target, key)
            }
        })
        delete proxy1.foo
        console.log(proxy1.foo, 'delete');
        effect(() => {
            console.log(proxy1.bar, 'effect函数执行 proxy1.bar');
        })

        function track (target, key) {
            if (!activeEffect) return
            let depsMap = bucket.get(target)
            if (!depsMap) {
                bucket.set(target, (depsMap = new Map()))
            }
            let deps = depsMap.get(key)
            if (!deps) {
                depsMap.set(key, (deps = new Set()))
            }
            deps.add(activeEffect)
            activeEffect.deps.push(deps)
        }

        function trigger (target, key) {
            let depsMap = bucket.get(target)
            if (!depsMap) return 
            let effects = depsMap.get(key)
            const effectsToRun = new Set(effects)
            effectsToRun && effectsToRun.forEach(effectFn => {
                if (effectFn.options && effectFn.options.scheduler) {
                    effectFn.options.scheduler(effectFn)
                } else {
                    effectFn()
                }
            })
        }

        function watch (source, cb, options = {}) {
            let getter
            if (typeof source === 'function') {
                getter = source
            } else {
                getter = () => traverse(source)
            }
            let newVal, oldVal
            let cleanup // 存储用户注册过的过期回调
            function onInvalidate (fn) {
                // 过期回调存储在cleanup中
                cleanup = fn
            }
            const job = function () {
                newVal = effectFn()
                // 调用回调函数cb之前，先调用过期回调
                if (cleanup) {
                    cleanup()
                }
                // 将onInvalidate作为回调函数的第三个参数，用户可以调用
                cb(newVal, oldVal, onInvalidate)
                oldVal = newVal
            }
            const effectFn = effect(
                () => getter(), 
                {
                    scheduler () {
                        if (options.flush === 'post') {
                            const p = Promise.resolve()
                            p.then(job)
                        } else {
                            job()
                        }
                    }
                }
            )
            
            if (options.immediate) {
                job()
            } else {
                // 手动调用副作用函数，拿到旧值
                oldVal = effectFn()
            }
        }

        function traverse (value, seen = new Set()) {
            // 如果数据是原始数据类型 或者已经侦听过，则return，防止死循环
            if (typeof value !== 'object' || value === null || seen.has(value)) return
            // 将数据添加到seen里面
            seen.add(value)

            // 暂不考虑数组等其他数据结构,假设就是对象
            for (const key in value) {
                traverse(value[key], seen)
            }
            return value
        }
        // watch(() => obj.bar, (newVal, oldVal) => {
        //     console.log(newVal, oldVal, 'newVal, oldVal数据变化了');
        // }, {
        //     immediate: true,
        //     flush: 'post'
        // })

        let finalData = 0;
        let count = 0
        // watch(() => obj.bar, async (newVal, oldVal) => {
        //     const res = await request({url: '/path/to/request'})
        //     finalData = res
        // })

        watch(() => obj.bar, async (newVal, oldVal, onInvalidate) => {
            // 定义一个标志，判断当前副作用函数是否过期，默认为false-没过期
            let expired = false

            onInvalidate(() => {
                // 调用onInvalidate函数，注册一个回调，当前副作用函数过期则将exipred改为true
                expired = true
            })

            const res = await new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve(newVal * 2)
                }, 1000)
            })
            // 只有没有过期才能进行赋值
            if (!expired) {
                finalData = res
                console.log(`Updated value: ${res}`);
            } else {
                console.log('The effect was invalidated');
            }
        })
    </script>
</body>
</html>